/**
 * @file bsp_flash.c
 * @brief FLASH封装驱动（兼容GD32）
 * @author wangh (wanghuan3037@fiberhome.com)
 * @version 1.0
 * @date 2020-05-09
 * @copyright Copyright (c) 2020  烽火通信
 ***************************************************************
 * @par 修改日志:
 * <table>
 * <tr><th>Date       <th>Version  <th>Author  <th>Description
 * <tr><td>2020-05-09 <td>1.0      <td>wangh   <td>内容
 * </table>
 */

#if 0
#if STM32_FLASH_SIZE<256
    #define STM32_SECTOR_SIZE 1024          //字节
#else
    #define STM32_SECTOR_SIZE	2048
#endif

uint16_t STM32_FLASH_BUF[STM32_SECTOR_SIZE/2];   //< 最多是2K字节
/**
 * @brief 从指定地址开始写入指定长度的数据
 * @param  WriteAddr        起始地址(此地址必须为2的倍数)
 * @param  pBuffer          数据指针
 * @param  NumToWrite       字节数
 */
void STM32_FLASH_Write(uint32_t WriteAddr, uint16_t *pBuffer, uint16_t NumToWrite)
{
    uint32_t secpos;        /* 扇区地址 */
    uint16_t secoff;        /* 扇区内偏移地址(16位字计算) */
    uint16_t secremain;     /* 扇区内剩余地址(16位字计算) */
     uint16_t i;    
    uint32_t offaddr;       /* 去掉0X08000000后的地址 */
    if(WriteAddr<STM32_FLASH_BASE||(WriteAddr>=(STM32_FLASH_BASE+1024*STM32_FLASH_SIZE)))return;    /* 非法地址 */
    
    NumToWrite = NumToWrite/2;  /* 将字节数换成半字(16位)数 */
    
    FLASH_Unlock();                             /* 解锁 */
    offaddr=WriteAddr-STM32_FLASH_BASE;         /* 实际偏移地址 */
    secpos=offaddr/STM32_SECTOR_SIZE;           /* 扇区地址  0~255 for STM32F103ZET6 */
    secoff=(offaddr%STM32_SECTOR_SIZE)/2;       /* 在扇区内的偏移(2个字节为基本单位) */
    secremain=STM32_SECTOR_SIZE/2-secoff;       /* 扇区剩余空间大小 */
    if(NumToWrite <= secremain) secremain=NumToWrite;   /* 不大于该扇区范围 */
    while(1)
    {
        STM32_FLASH_HalfReadN(secpos*STM32_SECTOR_SIZE+STM32_FLASH_BASE, STM32_FLASH_BUF, STM32_SECTOR_SIZE/2); /* 读出整个扇区的内容 */
        for(i=0; i<secremain; i++)  /* 校验数据 */
        {
            if(STM32_FLASH_BUF[secoff+i] != 0XFFFF)
                break;              /* 需要擦除 */
        }
        if(i < secremain)           /* 需要擦除 */
        {
            FLASH_ErasePage(secpos*STM32_SECTOR_SIZE + STM32_FLASH_BASE);   /* 擦除这个扇区 */
            for(i=0; i<secremain; i++)  /* 复制 */
            {
                STM32_FLASH_BUF[i+secoff] = pBuffer[i];
            }
            STM32_FLASH_Write_NoCheck(secpos*STM32_SECTOR_SIZE+STM32_FLASH_BASE, STM32_FLASH_BUF, STM32_SECTOR_SIZE/2); /* 写入整个扇区 */  
        }
        else
            STM32_FLASH_Write_NoCheck(WriteAddr, pBuffer, secremain);   /* 写已经擦除了的,直接写入扇区剩余区间 */
        if(NumToWrite==secremain)
            break;  /* 写入结束了 */
        else        /* 写入未结束 */
        {
            secpos++;                   /* 扇区地址增1 */
            secoff = 0;                 /* 偏移位置为0 */
               pBuffer += secremain;    /* 指针偏移 */
            WriteAddr += secremain;     /* 写地址偏移 */
               NumToWrite -= secremain; /* 字节(16位)数递减 */
            if(NumToWrite > (STM32_SECTOR_SIZE/2))
                secremain = STM32_SECTOR_SIZE/2;    /* 下一个扇区还是写不完 */
            else 
                secremain = NumToWrite;             /* 下一个扇区可以写完了 */
        }
    }
    FLASH_Lock();   /* 上锁 */
}
#endif



#include "bsp_flash.h"

#undef LOG_LEVEL_
#define LOG_LEVEL_ 	LOG_LVL_ASSERT
#include "bsp_log.h"

/**
 * @brief 获取指定地址的flash页边界
 * @param[in]  addr     地址
 * @return uint32_t 页边界地址
 */
static uint32_t flash_getPage( uint32_t addr, uint16_t page_size )
{
    return ((addr) & ~((page_size) - 1));
}

/**
 * @brief flash 擦除检查
 * @param[in]  addr     起始地址（应4字节对齐）
 * @param[in]  size     检查长度（应4字节倍数）
 * @return int8_t 
 * - @b Sucess : 0 无需擦除 \n
 * - @b Fail : -1 需要擦除
 */
static int8_t flash_erase_check( uint32_t addr, uint32_t size )
{
    uint32_t i;

    for (i = 0; i < size/4; i++)
    {
        if ( 0xFFFFFFFF != (*(volatile uint32_t *) addr) )
            return -1;
        addr += 4;
    }

    return 0;
}

/**
 * @brief flash 读
 * @param[in]  addr     起始地址
 * @param[in]  buf      数据缓存
 * @param[in]  size     字节数
 * @return int8_t 
 * - @b Sucess : 0 \n
 * - @b Fail : -1
 */
int8_t flash_read( uint32_t addr, void *buf, uint32_t size )
{
    uint32_t i;
    uint8_t *tmp = (uint8_t *)buf;

    if ( (addr < FLASH_START_ADDR) || ((addr + size) > FLASH_END_ADDR) || (buf == NULL) )
    {
        log_e("read outrange flash size! addr is (0x%p)", (void *)(addr + size));
        return -1;
    }

    for (i = 0; i < size; i++, tmp++, addr++)
    {
        *tmp = *(volatile uint8_t *)addr;
    }

    return 0;
}


int8_t flash_wite_no_erase( uint32_t addr, void *buf, uint32_t size )
{
    int8_t result = 0;
    uint32_t end_addr = addr + size;

    if ( (addr < FLASH_START_ADDR) || (end_addr > FLASH_END_ADDR) || (buf == NULL) || (addr % 4 != 0) )
        return -1;

    if ( ( (uintptr_t)buf % 4 != 0 ) || (size % 4 != 0) )
        return -1;

    uint32_t *tmp = (uint32_t *)buf;

#ifdef STM32F10X
    FLASH_Unlock();
    while (addr < end_addr)
    {
#ifdef STM32F10X_XL
        if (addr > 0x807FFFF)   /* bank2 */
            FLASH_ClearFlag( FLASH_FLAG_BANK2_PGERR | FLASH_FLAG_BANK2_WRPRTERR | FLASH_FLAG_BANK2_EOP );
        else
            FLASH_ClearFlag( FLASH_FLAG_PGERR | FLASH_FLAG_WRPRTERR | FLASH_FLAG_EOP );
#else
		FLASH_ClearFlag( FLASH_FLAG_PGERR | FLASH_FLAG_WRPRTERR | FLASH_FLAG_EOP );
#endif
		

        if ( FLASH_COMPLETE == FLASH_ProgramWord( addr, *((volatile uint32_t *)tmp) ) )
        {
            if ( *(volatile uint32_t *)addr != *(volatile uint32_t *)tmp )
            {
                result = -2;
                break;
            }
            addr += 4;
            tmp  += 4;
        }
        else
        {
            result = -3;
            break;
        }
    }
    FLASH_Lock();

#elif defined GD32F10X
    fmc_unlock();
    while (addr < end_addr)
    {
        if (addr > FMC_BANK0_END_ADDRESS)   /* bank2 */
        {
            fmc_flag_clear(FMC_FLAG_BANK1_END);
            fmc_flag_clear(FMC_FLAG_BANK1_WPERR);
            fmc_flag_clear(FMC_FLAG_BANK1_PGERR);
        }
        else
        {
            fmc_flag_clear(FMC_FLAG_BANK0_END);
            fmc_flag_clear(FMC_FLAG_BANK0_WPERR);
            fmc_flag_clear(FMC_FLAG_BANK0_PGERR);
        }

        if ( FMC_READY == fmc_word_program( addr, *((volatile uint32_t *)tmp) ) )
        {
            if ( *(volatile uint32_t *)addr != *(volatile uint32_t *)tmp )
            {
                result = -2;
                break;
            }
            addr += 4;
            tmp  += 4;
        }
        else
        {
            result = -3;
            break;
        }
    }
    fmc_lock();
#endif

    return result;
}


/**
 * @brief flash 写 \n
 * @TODO:接口未完成
 * @param[in]  addr     起始地址
 * @param[in]  buf      数据缓存
 * @param[in]  size     字节数
 * @return int8_t 
 * - @b Sucess : 0 \n
 * - @b Fail : -1
 */
int8_t flash_wite( uint32_t addr, void *buf, uint32_t size )
{
    if (0 == flash_erase_check( addr, size ))
        flash_wite_no_erase( addr, buf, size );
    else
        return -1;

    return 0;
}

int8_t flash_copy( uint32_t src_addr, uint32_t dest_addr, uint32_t size )
{
    if (0 != flash_erase_check( dest_addr, size ))
    {
        flash_erase( dest_addr, size );
    }

    flash_wite_no_erase( dest_addr, (void *)src_addr, size );
	
	return 0;
}


/**
 * @brief flash 擦除 \n
 * 会计算起始地址到所需擦除长度占用的所有扇区
 * @param[in]  addr     起始地址（4字节边界)
 * @param[in]  size     擦除长度（4字节倍数）
 * @return int8_t 
 * - @b Sucess : 0 \n
 * - @b Fail : -1
 */
int8_t flash_erase( uint32_t addr, uint32_t size )
{
    uint32_t page_addr;

    if ( (addr < FLASH_START_ADDR) || ((addr + size) > FLASH_END_ADDR) || (addr % 4 != 0) || (size % 4 != 0) )
    {
        log_e("read outrange flash size! addr is (0x%p)", (void *)(addr + size));
        return -1;
    }

#ifdef STM32F10X
    FLASH_Unlock();
    page_addr = flash_getPage( addr, FLASH_PAGE_SIZE );
    while ( page_addr < (addr + size) )
    {
#ifdef STM32F10X_XL
        if ( page_addr < 0x807FFFF )
            FLASH_ClearFlag( FLASH_FLAG_PGERR | FLASH_FLAG_WRPRTERR | FLASH_FLAG_EOP );
        else
            FLASH_ClearFlag( FLASH_FLAG_BANK2_PGERR | FLASH_FLAG_BANK2_WRPRTERR | FLASH_FLAG_BANK2_EOP );
#else
        FLASH_ClearFlag( FLASH_FLAG_PGERR | FLASH_FLAG_WRPRTERR | FLASH_FLAG_EOP );
#endif

        if ( FLASH_COMPLETE != FLASH_ErasePage( page_addr ) )
        {
            FLASH_Lock();
            return -1;
        }
        page_addr += FLASH_PAGE_SIZE;
    }
    FLASH_Lock();

#elif defined GD32F10X
    fmc_unlock();
    if (addr > FMC_BANK0_END_ADDRESS)
        page_addr = flash_getPage( addr, 0x1000 );
    else
        page_addr = flash_getPage( addr, FLASH_PAGE_SIZE );

    while ( page_addr < (addr + size) )
    {
        if ( page_addr < FMC_BANK0_END_ADDRESS )
        {
            fmc_flag_clear(FMC_FLAG_BANK0_END);
            fmc_flag_clear(FMC_FLAG_BANK0_WPERR);
            fmc_flag_clear(FMC_FLAG_BANK0_PGERR);
        }
        else
        {
            fmc_flag_clear(FMC_FLAG_BANK1_END);
            fmc_flag_clear(FMC_FLAG_BANK1_WPERR);
            fmc_flag_clear(FMC_FLAG_BANK1_PGERR);
        }

        if ( FMC_READY != fmc_page_erase( page_addr ) )
        {
            fmc_lock();
            return -1;
        }

        if (page_addr < FMC_BANK0_END_ADDRESS)
            page_addr += FLASH_PAGE_SIZE;
        else
            page_addr += (0x1000);
    }
    fmc_lock();
#endif

    return 0;
}

#if defined(STM32F10X_XL) || defined(GD32F10X_XD)
int8_t flash_erase_bank2( void )
{
    int8_t result = 0;

#ifdef STM32F10X_XL
    FLASH_UnlockBank2();
    FLASH_ClearFlag( FLASH_FLAG_BANK2_PGERR | FLASH_FLAG_BANK2_WRPRTERR | FLASH_FLAG_BANK2_EOP );
    if ( FLASH_COMPLETE != FLASH_EraseAllBank2Pages() )
        result = -1;
    FLASH_LockBank2();

#elif defined GD32F10X_XD
    fmc_bank1_unlock();
    fmc_flag_clear(FMC_FLAG_BANK1_END);
    fmc_flag_clear(FMC_FLAG_BANK1_WPERR);
    fmc_flag_clear(FMC_FLAG_BANK1_PGERR);
    if ( FMC_READY != fmc_bank1_erase() )
        result = -1;
    fmc_bank1_lock();
#endif

    return result;
}

#endif


/*********** (C) COPYRIGHT 2020 FiberHome *****END OF FILE****/
